﻿using DefaultDspPluginsLib;
using SoundEngine3;
using SoundEngine3.AudioProcessing;
using SoundEngine3.DspPlugins;
using SoundEngine3.DspPlugins.Meta;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using ZquenceStudio3.Core;
using ZquenceStudio3.Core.DspPlugins;
using ZquenceStudio3.DspPlugins;

namespace ZquenceStudio.DefaultDspPlugins.Effects
{
    public class DeplayDescription : IDspPlugin
    {
        public DspPluginInfo GetDspPluginInfo()
        {
            return new DspPluginInfo("Delay", DspType.Effect);
        }

        public BaseDspPluginInstance CreateDsp(ZquenceStudio3Context context, DspPluginInfo info)
        {
            return new Delay(context, info);
        }
    }

    public class Delay : CoreDspEffectPlugin
    {
        private IntPtr mDelayPtr;
        private DefaultDspPluginsLib.Depricated.Effect_Delay mDelayCs;

        public struct DelayData
        {
            public int channels;
            public int Length;
            public double Tempo;
            public double SamplesPerBeat;
        }

#if !Portable
        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern IntPtr Effect_Delay_Create();

        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern void Effect_Delay_Delete(IntPtr ptr);

        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern void Effect_Delay_Setup(IntPtr ptr, int samplesPerBeat, int sampleRate, double delayVolyme, double band1, double band2, double band3);

        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern void Effect_Delay_Clear(IntPtr ptr);

        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern void Effect_Delay_SetSampleRate(IntPtr ptr, int samplesPerBeat, int sampleRate);

        [DllImport(NativeSoundEngineDllImport.Dll)]
        public static extern void Effect_Delay_Process(IntPtr reverbPtr, ref DelayData data,
            ref double wav_in_left, ref double wav_in_right, ref double wav_out_left, ref double wav_out_right);
#endif
        private const double Band1Max = 0.8;
        private const double Band2Max = 0.8;
        private const double Band3Max = 0.8;
        private const double Band1Min = 0.0;
        private const double Band2Min = 0.0;
        private const double Band3Min = 0.0;
        private const int DelayTimeMax = 9;
        private const int DelayTimeMin = 1;
        private const double DelayVolymeMax = 0.9;
        private const double DelayVolymeMin = 0.0;

        private int mDelayTime = 3;
        private double mDelayVolyme = 0.6;
        private double mBand1 = 0.5;
        private double mBand2 = 0.5;
        private double mBand3 = 0.5;

        public Delay(ZquenceStudio3Context application, DspPluginInfo info)
            : base(application, info)
        {
#if !Portable
            mDelayPtr = Effect_Delay_Create();
#endif
            mDelayCs = new DefaultDspPluginsLib.Depricated.Effect_Delay();

            CreateParameters();

            SupportAudioProcessing = true;
        }

        ~Delay()
        {
#if !Portable
            Effect_Delay_Delete(mDelayPtr);
#endif
            mDelayCs = null;
        }

        private void CreateParameters()
        {
            // DelayTime.
            ParameterPropertiesDirect delayTime = new ParameterPropertiesDirect(this);
            delayTime.Label = "Delay time";
            delayTime.DirectGet += delegate() { return DelayTime; };
            delayTime.DirectSet += delegate(double value) { DelayTime = (int)value; };
            delayTime.MinValue = DelayTimeMin;
            delayTime.MaxValue = DelayTimeMax;
            delayTime.IsInteger = true;
            AddParameter(delayTime);

            // DelayVolyme.
            ParameterPropertiesDirect delayVolyme = new ParameterPropertiesDirect(this);
            delayVolyme.Label = "Delay volyme";
            delayVolyme.DirectGet += delegate() { return DelayVolyme; };
            delayVolyme.DirectSet += delegate(double value) { DelayVolyme = value; };
            delayVolyme.MinValue = DelayVolymeMin;
            delayVolyme.MaxValue = DelayVolymeMax;
            AddParameter(delayVolyme);

            // Band1.
            ParameterPropertiesDirect band1 = new ParameterPropertiesDirect(this);
            band1.Label = "Band 1";
            band1.DirectGet += delegate() { return Band1; };
            band1.DirectSet += delegate(double value) { Band1 = value; };
            band1.MinValue = Band1Min;
            band1.MaxValue = Band1Max;
            AddParameter(band1);

            // Band2.
            ParameterPropertiesDirect band2 = new ParameterPropertiesDirect(this);
            band2.Label = "Band 2";
            band2.DirectGet += delegate() { return Band2; };
            band2.DirectSet += delegate(double value) { Band2 = value; };
            band2.MinValue = Band2Min;
            band2.MaxValue = Band2Max;
            AddParameter(band2);

            // Band3.
            ParameterPropertiesDirect band3 = new ParameterPropertiesDirect(this);
            band3.Label = "Band 3";
            band3.DirectGet += delegate () { return Band3; };
            band3.DirectSet += delegate (double value) { Band3 = value; };
            band3.MinValue = Band3Min;
            band3.MaxValue = Band3Max;
            AddParameter(band3);

            Update();
        }

        public override Version Version
        {
            get { return DspPluginInfo.DefaultVersion; }
        }

        public override string PluginInfo
        {
            get { return DspPluginInfo.DefaultPluginInfo; }
        }
        
        public int DelayTime
        {
            get { return mDelayTime; }
            set
            {
                mDelayTime = value;
                Update();
            }
        }

        public double DelayVolyme
        {
            get { return mDelayVolyme; }
            set
            {
                mDelayVolyme = value;
                Update();
            }
        }

        public double Band1
        {
            get { return mBand1; }
            set
            {
                mBand1 = value;
                Update();
            }
        }

        public double Band2
        {
            get { return mBand2; }
            set
            {
                mBand2 = value;
                Update();
            }
        }

        public double Band3
        {
            get { return mBand3; }
            set
            {
                mBand3 = value;
                Update();
            }
        }

        private void Update()
        {
#if !Portable
            Effect_Delay_Setup(mDelayPtr, (int)SoundEngineContext.SamplesPerBeat, mDelayTime, mDelayVolyme, mBand1, mBand2, mBand3);
#endif
            mDelayCs.Update(SoundEngineContext.SamplesPerBeat, mDelayTime, mDelayVolyme, mBand1, mBand2, mBand3);
        }

        public override void Process(BaseAudioBuffer input, BaseAudioBuffer output)
        {
            double[] AudioBufferLeft = input.Channels[0].Samples;
            double[] AudioBufferRight = input.Channels[1].Samples;
            double[] AudioBufferLeft_ouput = output.Channels[0].Samples;
            double[] AudioBufferRight_output = output.Channels[1].Samples;

            DelayData data;
            data.channels = SoundEngineContext.OutputChannels;
            data.Length = AudioBufferLeft.Length;
            data.SamplesPerBeat = CoreContext.SoundEngine.Sequencer.SamplesPerBeat;
            data.Tempo = CoreContext.SoundEngine.Sequencer.Time.Tempo;

            if (NativeSoundEngineDllImport.UseBenchmarking)
            {
                ZquenceStudio3.Core.Utils.ProfilerMeter.Start1("Delay");
#if !Portable
                Effect_Delay_Process(mDelayPtr, ref data, ref AudioBufferLeft[0], ref AudioBufferRight[0], ref AudioBufferLeft_ouput[0], ref AudioBufferRight_output[0]);
#endif
                ZquenceStudio3.Core.Utils.ProfilerMeter.Stop1("Delay");
                ZquenceStudio3.Core.Utils.ProfilerMeter.Start2("Delay");
                mDelayCs.Process(ref data, AudioBufferLeft, AudioBufferRight, AudioBufferLeft_ouput, AudioBufferRight_output);
                ZquenceStudio3.Core.Utils.ProfilerMeter.Stop2("Delay");
            }
            else
            {
                if (NativeSoundEngineDllImport.UseNativeProcessing)
                {
#if !Portable
                    Effect_Delay_Process(mDelayPtr, ref data, ref AudioBufferLeft[0], ref AudioBufferRight[0], ref AudioBufferLeft_ouput[0], ref AudioBufferRight_output[0]);
#endif
                }
                else
                {
                    mDelayCs.Process(ref data, AudioBufferLeft, AudioBufferRight, AudioBufferLeft_ouput, AudioBufferRight_output);
                }
            }
        }
        
        protected override void OnChangeBufferSize(int bufferSize)
        {
#if !Portable
            Effect_Delay_Clear(mDelayPtr);
#endif
            mDelayCs.ClearBuffers();
        }

        protected override void OnChangeSampleRate(double sampleRate)
        {
#if !Portable
            Effect_Delay_SetSampleRate(mDelayPtr, (int)SoundEngineContext.SamplesPerBeat, (int)sampleRate);
#endif
            mDelayCs.SetSampleRate((int)sampleRate, (int)SoundEngineContext.SamplesPerBeat);
        }

        protected override void OnChangeInputChannels(int channels)
        {

        }

        protected override void OnChangeOutputChannels(int channels)
        {
            
        }

        protected override void OnProcessEvents(List<SoundEngine3.MIDI.Meta.MidiMessage> midiMessages)
        {

        }
    }
}
